package org.demo.main;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.demo.util.SysConstant;

import util.FileUtil;
import util.IntegerUtil;
import util.ParseUtils;
import util.PropertiesUtils;

public class InitConfig {
	//符号表
	public static Map<String, Object> symbolMap = new HashMap<String, Object>();
	
	public static int row = 1;
	//错误相关
	static boolean error = false;
	static String errMsg = null;
	public static void init() {
		//第一次初始化符号表的 关键字 运算符 界符
		initSymbolTable(SysConstant.KEYWORD, SysConstant.KEYWORD_SYMBOL);
		initSymbolTable(SysConstant.OPERTATOR, SysConstant.OPERATOR_SYMBOL);
		initSymbolTable(SysConstant.END, SysConstant.END_SYMBOL);
		initSymbolTable(SysConstant.NUM, SysConstant.NUM_SYMBOL);
		initSymbolTable(SysConstant.IDENTITY, SysConstant.IDNETIFY_SYMBOL);
		//symbolMap.forEach((x, y)-> System.out.println(x + "\t" + y));
		
	}

	public static void analyze(char[] codes) {
		FileUtil.writeFile("单词\t\t\t\t\t标识码\t\t\t\t含义", FileUtil.CF_OUTPUT ,false);
		
		int n = codes.length;
		int i = 0;
		while (i < n){
			char ch = codes[i];
			//当前行界符
			if (ParseUtils.isCurLineOfEnd(ch)) {
				i++;
			//换行界符 用于记录错误所在行
			} else if (ParseUtils.isLineChaneOfEnd(ch)) {
				//System.out.println("换行-->" + ch);
				row ++;
				i += 2;
			//开始处理
			} else {
				//返回下一次i的位置
				try {
					i = dealWithChar(i, codes);
					if (error) {
						System.err.println(errMsg);
						error = false;
					}
				} catch (Exception e) {
					//e.printStackTrace();
					//继续处理
					System.err.println(e.getMessage());
				}
			}	
		
		}
	}
	
	private static int dealWithChar(int i, char[] codes) throws Exception {
		if (i < 0 || i >= codes.length)  
			throw new Exception("ArrayOutOfBound exception: line" + row + "\t" + i);
		
		StringBuilder res = new StringBuilder();
		char ch = codes[i];
		res.append(ch);
		
		 if (ParseUtils.isLetter(ch)) { 
			 return handleFirstAlpha(i, res, codes);
		 } else if (ParseUtils.isDigit(ch)) {
			 return handleFirstDigit(i, res, codes);
		 } else {
			//i++
			if (ParseUtils.isLineChaneOfEnd(ch) 
					|| ParseUtils.isCurLineOfEnd(ch))
				return ++i;
			//单界符 双界符 .
			if (ParseUtils.isWordEnd(ch) || ParseUtils.isWordEnds(ch) || ch == '.') {
				String result = String.format("%-20s%5s%20s\n", 
						res.toString(), symbolMap.get(res.toString()), "end");
				FileUtil.writeFile(result, FileUtil.CF_OUTPUT);
				
				return ++i;
			}
			//转义符
			if (ch == '\\') {
				char c = codes[i + 1];
				if (c == 'n' || c == 't' || c == 'r' || ch =='0') {
					//转义留作日后扩展
					//return i + 2;
				}
			}
			//单字符
			if (ch == '\'') {
				
			}
			//字符串
			if (ch == '\"') {
				
			}
			//注释处理
			if (ch == '/') {
				if (codes[i + 1] == '*') {
					return dealWithMoreNote(i + 1, res, codes);
				} else if(codes[i + 1] == '/') {
					return dealWithSingleNode(i + 1, res, codes);
				}
			}
			//操作符
			if (ParseUtils.isOps(ch)) {
				char[] opsChars = new char[2];
				opsChars[0] = ch;
				opsChars[1] = codes[i + 1];
				String result = null;
				//双操作符
				if (ParseUtils.isOps(opsChars)) {
					res.append(opsChars[1]);
				    result = String.format("%-20s%5s%20s\n", 
							res.toString(), symbolMap.get(res.toString()), "operator(more)");
					i+=2;
				//单操作数
				} else {
					result = String.format("%-20s%5s%20s\n", 
							res.toString(), symbolMap.get(res.toString()), "operator(single)");
					i++;
				}
				
				FileUtil.writeFile(result,  FileUtil.CF_OUTPUT);
				return i;
			}
			i++;	
		 }
		return i;
				
	}
	
	//单行注释
	private static int dealWithSingleNode(int i, StringBuilder res, char[] codes) throws Exception {
		StringBuilder sb = new StringBuilder(res);
		sb.append(codes[i]);
		char ch = codes[++i];
		while(!ParseUtils.isLineChaneOfEnd(ch) && ch != '\0') {
			sb.append(ch);
			ch = codes[++i];
		}
		//注释
		System.out.println("catch single Note:" + sb.toString());
		return i;
	}

	//多行注释
	private static int dealWithMoreNote(int i, StringBuilder res, char[] codes) throws Exception {
		StringBuilder sb = new StringBuilder(res);
		sb.append(codes[i]);
		char ch = codes[++i];
		while((i+1) < codes.length && (codes[i + 1] != '/') && ch != '*') {
			sb.append(ch);
			if (ParseUtils.isLineChaneOfEnd(ch)) {
				ch = codes[i += 2];
			} else 
				ch = codes[++i];
		}
		//调试: System.out.println(ch);
		//要么遍历结束都没有关闭 要么只有* 而没有/ 要么只有/
		if (i+1 == codes.length || (codes[i+1] != '/') || (codes[i] != '*' && codes[i+1] == '/')) {
			errMsg  = "/* not close ! start in line: " + row + " but not found */";
			error = true;
			return i;		
		}
		sb.append("*/");
		//注释
		System.out.println("catch more note:" + sb.toString());
		return i + 2;
	}

	//处理首字符是数字的情况
	private static int handleFirstDigit(int i, StringBuilder first, char[] codes) throws Exception {
		StringBuilder sb = new StringBuilder(first);
		char ch = codes[++i];
		while(ParseUtils.isDigit(ch)) {
			sb.append(ch);
			ch = codes[++i];
		}
		//错误结果
		String result = null;
		String keyName = null;
		//整数
		if (ParseUtils.isEnd(ch)){
			//i++;
			keyName = SysConstant.NUM + "(integer)";
		//科学计数法	
		} else if (ch == 'E' || ch == 'e') {
		   sb.append(ch);
		   int j  = i + 1;
		   
		   sb.append(codes[j]);
		   if (codes[j] == '+' || codes[j] == '-' || ParseUtils.isDigit(codes[j])) {
			   int k = j + 1;
			   char c = codes[k];
			   while(ParseUtils.isDigit(c)) {
				   sb.append(c);
				   c = codes[++k];
			   }
			   if (ParseUtils.isEnd(c)) {
				   i = k;
				   keyName = SysConstant.NUM + "(E/e)";
			   } else {
				   sb.append(c);
				   i = jumpErrWord(i, codes, sb);
				   result = sb.toString() + " is not an invalid E/e: line "  + row;
			   }
			
		   } else {
			   i = jumpErrWord(i, codes, sb);
			   result = sb.toString() + " is not an invalid E/e, in line: "  + row;
		   }
	   
		//浮点数	
		} else if (ch == '.' && ParseUtils.isDigit(codes[i + 1])) {
			sb.append(ch);
			char c = codes[++i];
			while (ParseUtils.isDigit(c)) {
				sb.append(c);
				c = codes[++i];
			}
			if (ParseUtils.isEnd(c)) {
				keyName = SysConstant.NUM + "(float)";
			} else {
				sb.append(c);
				i = jumpErrWord(i, codes, sb);
				result = sb.toString() + " is not an invalid float literal number, in line: " + row;
			}
		  
		//异常
		} else {
			sb.append(ch);
			i = jumpErrWord(i, codes, sb);
			//error = true;
			result = sb.toString() + " is not an invalid identity, in line: " + row;
		}
		if (!ParseUtils.isEmpty(result)) {
			errMsg = result;
			error = true;
			return i;
			//throw new Exception(result);
		}
		
		String value = IntegerUtil.toLiteralString(
					   	Double.valueOf(sb.toString()), 
					   	2, 0);
		symbolMap.put(sb.toString(), value);
		//小数只打印后3位 但不影响最终语法分析阶段获取
		int point = value.indexOf(".");
		if (point != -1) {
			value = value.substring(0, point + 5);
		}
	
		String res = String.format("%-20s%5s%20s\n", 
				sb.toString(),	value, keyName);
		FileUtil.writeFile(res,  FileUtil.CF_OUTPUT);
		
		return i;
	}

	/**
	 * 跳跃错误分词进入下一个分词阶段  以终结符end划分
	 * @param i
	 * @param codes
	 * @param sb
	 * @return
	 */
	private static int jumpErrWord(int i, char[] codes, StringBuilder sb) {
		char ch = codes[++i];
		while(!ParseUtils.isEnd(ch)) {
			sb.append(ch);
			ch = codes[++i];
		}
		return i;
	}

	 //处理首字母是字母的情况
	private static int handleFirstAlpha(int i, StringBuilder res, char[] codes) {
		char ch = codes[++i];
		StringBuilder sb = new StringBuilder(res);
		while(ParseUtils.isDigit(ch) || ParseUtils.isLetter(ch)) {
			sb.append(ch);
			ch = codes[++i];
		}
	
		String keyName = null;
		if (isKeyWord(sb.toString())) {
			keyName = SysConstant.KEYWORD;
		} else {
			int start = countCurrentType(SysConstant.IDNETIFY_SYMBOL);
			symbolMap.put(sb.toString(), start); 
			keyName = SysConstant.IDENTITY;
		}

		String result = String.format("%-20s%5s%20s\n", 
						sb.toString(), symbolMap.get(sb.toString()), keyName);
		FileUtil.writeFile(result,  FileUtil.CF_OUTPUT);
		return i;
		
	}

	/**
	 * 统计当前类型的所有数据量并返回
	 * @param start
	 * @return
	 */
	public static int countCurrentType(int start) {
		int end = 0;
		 switch (start) {
			case SysConstant.KEYWORD_SYMBOL:
				end = SysConstant.OPERATOR_SYMBOL;
				break;
			case SysConstant.OPERATOR_SYMBOL:
				end = SysConstant.END_SYMBOL;
				break;
			case SysConstant.END_SYMBOL:
				end = SysConstant.NUM_SYMBOL;
				break;
			case SysConstant.NUM_SYMBOL:
				end = SysConstant.IDNETIFY_SYMBOL;
				break;
			case SysConstant.IDNETIFY_SYMBOL:
				end = Integer.MAX_VALUE;
				break;
			default:
				break;
		}
		int count = 0;
		Collection<Object> values = symbolMap.values();
		for (Object val : values) {
			if (!(val instanceof Integer)) {
				continue;
			}
			Integer temp  = (Integer) val;
			if (end > temp && temp >= start)
				++count;
		}
		return (count + start);
	}
	
	private static boolean isKeyWord(String str) {
		Integer value = (Integer) symbolMap.get(str);
		return (value != null) && 
				( SysConstant.OPERATOR_SYMBOL > value  && value >= SysConstant.KEYWORD_SYMBOL);
	}

	
	/**
	 * 初始化符号表
	 * @param initParam 初始化配置的参数
	 * @param symbol 初始化参数对应的机内码
	 */
	private static void initSymbolTable(String initParam, int symbol) {
		if(ParseUtils.isEmpty(initParam) || !SysConstant.isValidConstant(initParam)) {
			throw new RuntimeException("参数[" + initParam + "]无法被初始化");
		}
		//空格切分配置文件的value
		String[] init = PropertiesUtils.getValue(initParam).split(" ");
		//同参数类别初始化
		symbolMap.put(initParam, symbol);
		//同类别下再分类
		for (int i = 1 ; i < init.length ; i++) {
			symbolMap.put(init[i], symbol + i);
		}
	}
	
}
